%%%-------------------------------------------------------------------
%%% @author wukai
%%% @copyright (C) 2017, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 25. 十月 2017 11:34
%%%-------------------------------------------------------------------
-module(frame_conn).
-author("wukai").
-include("../include/mqtt_protocol.hrl").

%% 客户端标识符，遗嘱主题，遗嘱消息，用户名，密 码。
%% API
-export([do_command/3]).
do_command(Ref, State, Frame) when Frame#mqtt_frame.frame_flag == 0 ->
  if
    State#state.connected == true -> mqtt_process:exit_self();
    true -> do_nothing
  end,

  <<Len:?USHORT, Pname:Len/binary, PVersion:?UBYTE, Uflag:1,
    Pflag:1, WillRetain:1, WillQos:2, WillFlag:1, CleanSession:1,
    Reserved:1, KeepAlive:?USHORT, IDLen:16, ClientID:IDLen/binary,
    Playload/binary>> = Frame#mqtt_frame.left_bytes,

  lager:log(info, self(), "
  vName Len:~p ,VName:~p,version:~p UFLAG: ~p,PFLAG:~p,

  WillRetain:~p,WillQos:~p,WillFlag:~p

  CleanSession:~p,Reserved:~p,KeepAlive:~p,ClientID:~p~n
  ",
    [Len, Pname, PVersion, Uflag, Pflag, WillRetain,
      WillQos, WillFlag, CleanSession, Reserved, KeepAlive,
      ClientID
    ]
  ),

  protocol(Pname, PVersion, Ref, State),
  clientId(ClientID, CleanSession),
  {Will, Left} = will(WillFlag, Playload),
  {Username, Password} = user(Uflag, Left),

  case mqtt_user:login({Username, Password}) of
    true -> log:log("login seucess");
    false -> conn_ack(Ref, State, 5), mqtt_process:exit_self()
  end,
  conn_ack(Ref, State, 0),
  Client = {Username, get_client_id(ClientID)},
  Will_Publish = case Will of
                   {TLen, Topic, Con} -> publish_frame:wrap(Client, Topic, TLen, Con, 0, WillQos, WillRetain);
                   Other -> Other
                 end,
  session_server:send_online(Client, self(), CleanSession, Will_Publish),

  if CleanSession == 0 -> cache_server:send_send(Client, self());true -> ok end,
  if WillFlag == 1, WillRetain == 1 -> retain_server:retain_save(Client, Will, Will_Publish);true -> do_nothing end,

  State#state{
    keep_alive = KeepAlive * 15 * 100,
    client = Client, connected = true,
    mqtt_version = PVersion,
    clean_session = CleanSession,
    will_falg = WillFlag,
    will_publish = Will_Publish
  };

do_command(_, _, _) ->
  lager:log(info, self(), "cmd flag error"),
  exit(ok).

protocol(<<"MQTT">>, 4, _, _) ->
  lager:log(info, self(), "mqtt protocol 3.1.1 ok");
protocol(<<"MQTT">>, _, Ref, State) ->
  conn_ack(Ref, State, 1),
  lager:log(info, self(), "protocol error"),
  mqtt_process:exit_self();

protocol(_, _, Ref, State) ->
  conn_ack(Ref, State, 1),
  lager:log(info, self(), "connect msg error"),
  mqtt_process:exit_self().

%%获取遗嘱主题
will(Flag, Bin) ->
  case Flag of
    1 ->
      <<TopLen:16, Topic:TopLen/binary, MsgLen:16, Con:MsgLen/binary, Left/binary>> = Bin,
      {{TopLen, Topic, Con}, Left};
    0 -> {undefined, Bin}
  end.

%%获取用户名密码
user(Flag, Bin) ->
  case Flag of
    1 ->
      <<UserLen:16, UserName:UserLen/binary, PassLen:16, Password:PassLen/binary>> = Bin,
      lager:log(info, self(), "username:  ~p password msg ~p", [UserName, Password]),
      {binary_to_list(UserName), binary_to_list(Password)};
    0 -> {undefine, undefine}
  end.

%%CODE 0 链接已确认
%%CODE 1 链接已拒绝，不支持协议版本
%%CODE 2 链接客户端标识不正确
%%CODE 3 服务端不可用
%%CODE 4 用户名密码格式不对 用于校验
%%CODE 5 未授权 登录失败

conn_ack(Ref, State, Code) ->
  Bin = <<?FRAME_CONN_ACK:4, 0:4, 2:8, 0:8, Code:8>>,
  SFlag = mqtt_process:send_binary(State, Bin),
  lager:log(info, self(), "send  conn ack data ~p", [SFlag]).

%%若clientId的长度为0 则必须cleanSession为1 不保存会话
clientId(ClientID, CleanSession) when byte_size(ClientID) == 0 andalso CleanSession == 0 ->
  mqtt_process:exit_self();
clientId(ClientID, CleanSession) ->
  log:log("clientId/~p cleansession/~p", [ClientID, CleanSession]).

get_client_id(ClientID) when byte_size(ClientID) == 0 ->
  quickrand:seed(),
  {_, _, _, _, UUID} = uuid:new(self()),
  log:log("uuid1:~p", [UUID]),
  string:concat("Client_", integer_to_list(UUID));

get_client_id(ClientID) ->
  erlang:binary_to_list(ClientID).